﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2013-08-25 14:15
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using BassDrop.Core.Common;
using BassDrop.Core.Common.Localization;
using BassDrop.Core.Common.Media;
using BassDrop.Core.Common.Media.Playlists;

namespace BassDrop.DesktopClient.UI.Panels
{
	/// <summary>
	/// Interaction logic for MediaPlayerPanel.xaml
	/// </summary>
	public partial class MediaPlayerPanel : UserControl
	{
		/// <summary>
		/// The currently active playlist
		/// </summary>
		public Playlist CurrentPlaylist
		{
			get
			{
				return (Playlist) GetValue( CurrentPlaylistProperty );
			}
			set
			{
				SetValue( CurrentPlaylistProperty, value );
			}
		}

		// Using a DependencyProperty as the backing store for CurrentPlaylist.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty CurrentPlaylistProperty =
			DependencyProperty.Register( "CurrentPlaylist", typeof( Playlist ), typeof( MediaPlayerPanel ), new PropertyMetadata( null ) );

		/// <summary>
		/// The currently active track
		/// </summary>
		private PlaylistItemBase m_currentTrack = null;

		/// <summary>
		/// Available time formats
		/// </summary>
		private static readonly string[] s_TimeFormats = 
		{
			"{0:0}:{1:00}"
		};// TODO: multiple formats, switch onclick

		/// <summary>
		/// Selected time format
		/// </summary>
		private string m_timeFormat = s_TimeFormats[0];

		/// <summary>
		/// Constructor
		/// </summary>
		public MediaPlayerPanel()
		{
			InitializeComponent();

			if ( Controller.Instance != null ) // check needed to make the designer work
			{
				BindPlaylist();

				Controller.Instance.PlaylistChanged += Controller_PlaylistChanged;
			}

			Strings.StaticPropertyChanged += Strings_StaticPropertyChanged;
		}

		/// <summary>
		/// Sets up bindings with the current playlist, if any
		/// </summary>
		private void BindPlaylist()
		{
			CurrentPlaylist = Controller.Instance.NowPlaying;

			BindTrack();

			if ( CurrentPlaylist != null )
			{
				CurrentPlaylist.TrackChanged -= m_currentPlaylist_TrackChanged;
				CurrentPlaylist.TrackChanged += m_currentPlaylist_TrackChanged;
				((INotifyPropertyChanged) CurrentPlaylist).PropertyChanged -= m_currentPlaylist_PropertyChanged;
				((INotifyPropertyChanged) CurrentPlaylist).PropertyChanged += m_currentPlaylist_PropertyChanged;
			}
		}

		/// <summary>
		/// Deletes bindings with the old playlist, if any
		/// </summary>
		private void UnbindPlaylist()
		{
			if ( CurrentPlaylist != null )
			{
				CurrentPlaylist.TrackChanged -= m_currentPlaylist_TrackChanged;
				((INotifyPropertyChanged) CurrentPlaylist).PropertyChanged -= m_currentPlaylist_PropertyChanged;
			}

			CurrentPlaylist = null;
		}

		/// <summary>
		/// Sets up bindings with the current track, if any
		/// </summary>
		private void BindTrack()
		{
			m_currentTrack = CurrentPlaylist.SelectedItem;

			UpdateTrackInfo();
			UpdateTrackPlayState();
			UpdateTrackPosition();

			if ( m_currentTrack != null )
			{
				m_currentTrack.PropertyChanged += m_currentTrack_PropertyChanged;
				m_currentTrack.PlayStateChanged += m_currentTrack_PlayStateChanged;
				if ( m_currentTrack.MediaPlayer != null )
				{
					m_currentTrack.MediaPlayer.PositionChanged += MediaPlayer_PositionChanged;
				}
			}
		}

		/// <summary>
		/// Deletes bindings with the old track, if any
		/// </summary>
		private void UnbindTrack()
		{
			if ( m_currentTrack != null )
			{
				m_currentTrack.PropertyChanged -= m_currentTrack_PropertyChanged;
				m_currentTrack.PlayStateChanged -= m_currentTrack_PlayStateChanged;
				if ( m_currentTrack.MediaPlayer != null )
				{
					m_currentTrack.MediaPlayer.PositionChanged -= MediaPlayer_PositionChanged;
				}
			}

			m_currentTrack = null;

			UpdateTrackInfo();
			UpdateTrackPlayState();
			UpdateTrackPosition();
		}

		/// <summary>
		/// Updates the UI with the info of the current track
		/// </summary>
		private void UpdateTrackInfo()
		{
			// TODO: UpdateTrackInfo()
			if ( m_currentTrack == null )
			{
				TextBlockTitle.Text = "-"; // TODO: custom controlok: jobbra-balra csúszka, auto-tooltip
				TextBlockArtist.Text = "-";
				TextBlockAlbum.Text = "-";
				TextBlockOtherInfo.Text = "-"; // TODO: custom control: szöveglista, auto-váltogatás, tegye bele az errort, ha van
				// TODO: rating - http://www.codeproject.com/Articles/45210/WPF-A-Simple-Yet-Flexible-Rating-Control
				TextBlockFullTime.Text = string.Format( "/ " + m_timeFormat, "-", "--" );
			}
			else
			{
				var track = m_currentTrack as PlaylistItemMediaTrack;

				if ( track != null )
				{
					if ( track.Track.TrackMetadata == null )
					{
						TextBlockTitle.Text = string.Format( "{0}: {1}", Strings.LabelMetadataTitle, track.Track.FileName + track.Track.FileExtension );
						TextBlockArtist.Text = string.Format( "{0}: {1}", Strings.LabelMetadataArtist, "-" );
						TextBlockAlbum.Text = string.Format( "{0}: {1}", Strings.LabelMetadataAlbum, "-" );
						TextBlockOtherInfo.Text = ""; // TODO: custom control: szöveglista, auto-váltogatás, tegye bele az errort, ha van
						// TODO: rating
					}
					else
					{
						TextBlockTitle.Text = string.Format( "{0}: {1}", Strings.LabelMetadataTitle, track.Track.TrackMetadata.Title ?? track.Track.FileName + track.Track.FileExtension );
						TextBlockArtist.Text = string.Format( "{0}: {1}", Strings.LabelMetadataArtist, track.Track.TrackMetadata.Artist ?? "-" );
						TextBlockAlbum.Text = string.Format( "{0}: {1}", Strings.LabelMetadataAlbum, track.Track.TrackMetadata.Album ?? "-" );
						TextBlockOtherInfo.Text = ""; // TODO: custom control: szöveglista, auto-váltogatás, tegye bele az errort, ha van
						// TODO: rating
					}

					if ( track.Length == null )
					{
						TextBlockFullTime.Text = string.Format( "/ " + m_timeFormat, "-", "--" );
					}
					else
					{
						int seconds = (int) (((long) track.Length) / 1000) % 60;
						int minutes = (int) (((long) track.Length) / 60000);

						TextBlockFullTime.Text = string.Format( "/ " + m_timeFormat, minutes, seconds );
					}
				}

				// TODO: action items
			}
		}

		/// <summary>
		/// Updates the playstate indicator based on the track
		/// </summary>
		private void UpdateTrackPlayState()
		{
			if ( m_currentTrack == null )
			{
				ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Stopped"] as Style;
				return;
			}

			switch ( m_currentTrack.CurrentPlayState )
			{
				case PlaylistItemBase.PlayState.Stopped:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Stopped"] as Style;
					break;
				case PlaylistItemBase.PlayState.Paused:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Paused"] as Style;
					break;
				case PlaylistItemBase.PlayState.Playing:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Playing"] as Style;
					break;
				case PlaylistItemBase.PlayState.Seeking:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Seeking"] as Style;
					break;
				case PlaylistItemBase.PlayState.Finished:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Finished"] as Style;
					break;
				case PlaylistItemBase.PlayState.Error:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Error"] as Style;
					break;
				default:
					ImagePlayState.Style = App.Current.Resources["StyleMediaPlayerPanelImagePlayState_Stopped"] as Style;
					break;
			}
		}

		/// <summary>
		/// Updates the progress slider based on the track
		/// </summary>
		private void UpdateTrackPosition()
		{
			if ( m_currentTrack == null
				|| m_currentTrack.MediaPlayer == null
				|| m_currentTrack.MediaPlayer.Position == null )
			{
				SliderProgress.ValueChanged -= SliderProgress_ValueChanged;
				SliderProgress.Value = 0;
				SliderProgress.ValueChanged += SliderProgress_ValueChanged;
				SliderProgress.IsEnabled = false; // TODO: tüntesse el a thumbot

				TextBlockTime.Text = string.Format( m_timeFormat, "-", "--" );
				return;
			}

			SliderProgress.IsEnabled = true;
			SliderProgress.ValueChanged -= SliderProgress_ValueChanged;
			SliderProgress.Value = (double) m_currentTrack.MediaPlayer.PercentPosition;
			SliderProgress.ValueChanged += SliderProgress_ValueChanged;

			int seconds = (int) (((long) m_currentTrack.MediaPlayer.Position) / 1000) % 60;
			int minutes = (int) (((long) m_currentTrack.MediaPlayer.Position) / 60000);

			TextBlockTime.Text = string.Format( m_timeFormat, minutes, seconds );
		}

		#region Event handlers

		private void Strings_StaticPropertyChanged( object sender, PropertyChangedEventArgs e )
		{
			if ( e.PropertyName == "Strings" )
			{
				UpdateTrackInfo();
			}
		}

		private void Controller_PlaylistChanged( object sender, PropertyChangedEventArgs e )
		{
			Dispatcher.BeginInvoke( (Action) (() =>
			{
				UnbindPlaylist();
				BindPlaylist();
			}), null );
		}

		private void m_currentPlaylist_TrackChanged( object sender, PropertyChangedEventArgs e )
		{
			Dispatcher.BeginInvoke( (Action) (() =>
			{
				UnbindTrack();
				BindTrack();
			}), null );
		}

		private void m_currentPlaylist_PropertyChanged( object sender, PropertyChangedEventArgs e )
		{
			if ( e.PropertyName == "IsPlaying" )
			{
				Dispatcher.BeginInvoke( (Action) (() =>
					{
						UnbindTrack();
						BindTrack();
					}), null );
			}
		}

		private void m_currentTrack_PlayStateChanged( object sender, PropertyChangedEventArgs e )
		{
			Dispatcher.BeginInvoke( (Action) (() =>
			{
				UpdateTrackPlayState();
			}), null );
		}

		void MediaPlayer_PositionChanged( object sender, PropertyChangedEventArgs e )
		{
			Dispatcher.BeginInvoke( (Action) (() =>
			{
				UpdateTrackPosition();
			}), null );
		}

		void m_currentTrack_PropertyChanged( object sender, PropertyChangedEventArgs e )
		{
			Dispatcher.BeginInvoke( (Action) (() =>
			{
				UpdateTrackInfo();
			}), null );
		}

		private void ButtonPreviousTrack_Click( object sender, RoutedEventArgs e )
		{
			if ( CurrentPlaylist != null )
			{
				CurrentPlaylist.Previous();
			}
		}

		private void ButtonPlayPause_Click( object sender, RoutedEventArgs e )
		{
			if ( CurrentPlaylist != null )
			{
				if ( m_currentTrack == null )
				{
					CurrentPlaylist.Play();
				}
				else
				{
					if ( m_currentTrack.CurrentPlayState == PlaylistItemBase.PlayState.Playing
						|| m_currentTrack.CurrentPlayState == PlaylistItemBase.PlayState.Seeking )
					{
						CurrentPlaylist.Pause();
					}
					else
					{
						CurrentPlaylist.Play();
					}
				}
			}
		}

		private void ButtonStop_Click( object sender, RoutedEventArgs e )
		{
			if ( CurrentPlaylist != null )
			{
				CurrentPlaylist.Stop();
			}
		}

		private void ButtonNextTrack_Click( object sender, RoutedEventArgs e )
		{
			if ( CurrentPlaylist != null )
			{
				CurrentPlaylist.Next();
			}
		}

		private void ButtonRepeat_Click( object sender, RoutedEventArgs e )
		{
			if ( CurrentPlaylist != null )
			{
				switch ( CurrentPlaylist.RepeatMode )
				{
					case Playlist.RepeatModeType.NoRepeat:
						CurrentPlaylist.RepeatMode = Playlist.RepeatModeType.RepeatAll;
						break;
					case Playlist.RepeatModeType.RepeatOne:
						CurrentPlaylist.RepeatMode = Playlist.RepeatModeType.NoRepeat;
						break;
					case Playlist.RepeatModeType.RepeatAll:
						CurrentPlaylist.RepeatMode = Playlist.RepeatModeType.RepeatOne;
						break;
				}
			}
		}

		private void ButtonMute_Click( object sender, RoutedEventArgs e )
		{
			// TODO: ButtonMute_Click()
		}

		private void SliderProgress_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
		{
			if ( m_currentTrack != null && m_currentTrack.MediaPlayer != null )
			{
				m_currentTrack.MediaPlayer.PercentPosition = SliderProgress.Value;
			}
		}

		private void SliderVolume_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
		{
			// TODO: SliderVolume_ValueChanged()
		}

		#endregion Event handlers
	}

	#region Value converters

	/// <summary>
	/// Gets whether the RepeatModeType signals some kind of repetition
	/// </summary>
	public class RepeatModeTypeConverter : IValueConverter
	{

		#region IValueConverter Members

		public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
		{
			return (value as Playlist.RepeatModeType? ?? Playlist.RepeatModeType.NoRepeat) != Playlist.RepeatModeType.NoRepeat;
		}

		public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
		{
			throw new NotImplementedException();
		}

		#endregion
	}

	#endregion Value converters
}
